package ${packagePrefix}.common.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Consumer;

/**
 * Excel导入导出工具类
 * <p>
 * 基于EasyExcel的Excel处理工具，支持Excel文件读取和导出功能
 * </p>
 *
 * @author Auto Generated
 */
@Slf4j
public class ExcelHelper {

    /**
     * 从Excel文件读取数据
     *
     * @param file      Excel文件
     * @param dataClass 目标数据类型
     * @param consumer  数据处理函数
     * @param <T>       数据类型
     * @return 读取的数据条数
     */
    public static <T> int read(MultipartFile file, Class<T> dataClass, Consumer<List<T>> consumer) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("Excel文件不能为空");
        }

        String originalFilename = file.getOriginalFilename();
        ExcelDataListener<T> listener = new ExcelDataListener<>(consumer);

        try (InputStream inputStream = file.getInputStream()) {
            EasyExcel.read(inputStream, dataClass, listener)
                    .excelType(getExcelType(originalFilename))
                    .sheet()
                    .doRead();
            return listener.getTotal();
        } catch (IOException e) {
            log.error("读取Excel文件失败", e);
            throw new RuntimeException("读取Excel文件失败: " + e.getMessage());
        }
    }

    /**
     * 导出Excel数据到HTTP响应
     *
     * @param response  HTTP响应对象
     * @param data      要导出的数据列表
     * @param dataClass 数据类型
     * @param fileName  导出的文件名(不包含扩展名)
     * @param <T>       数据类型
     */
    public static <T> void export(HttpServletResponse response,
                                 List<T> data,
                                 Class<T> dataClass,
                                 String fileName) {
        export(response, data, dataClass, fileName, null, null);
    }

    /**
     * 导出Excel数据到HTTP响应，支持自定义表头和样式处理器
     *
     * @param response      HTTP响应对象
     * @param data          要导出的数据列表
     * @param dataClass     数据类型
     * @param fileName      导出的文件名(不包含扩展名)
     * @param sheetName     工作表名称，为空则使用"Sheet1"
     * @param writeHandlers 自定义样式处理器列表，可为null
     * @param <T>           数据类型
     */
    public static <T> void export(HttpServletResponse response,
                                 List<T> data,
                                 Class<T> dataClass,
                                 String fileName,
                                 String sheetName,
                                 List<WriteHandler> writeHandlers) {
        try {
            // 设置响应头
            setExcelResponseHeaders(response, fileName);
            
            // 构建Excel写入器
            ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream(), dataClass)
                    .excelType(ExcelTypeEnum.XLSX)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
            
            // 添加自定义样式处理器
            if (writeHandlers != null && !writeHandlers.isEmpty()) {
                for (WriteHandler writeHandler : writeHandlers) {
                    writerBuilder.registerWriteHandler(writeHandler);
                }
            }
            
            // 写入数据
            writerBuilder.sheet(StringUtils.hasText(sheetName) ? sheetName : "Sheet1")
                    .doWrite(data);
                    
        } catch (IOException e) {
            log.error("导出Excel文件失败", e);
            throw new RuntimeException("导出Excel文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 导出多个工作表的Excel数据到HTTP响应
     *
     * @param response     HTTP响应对象
     * @param fileName     导出的文件名(不包含扩展名)
     * @param sheetDataMap 工作表数据映射，key为工作表名称，value为SheetData对象
     */
    public static void exportMultiSheet(HttpServletResponse response, 
                                      String fileName,
                                      Map<String, SheetData<?>> sheetDataMap) {
        try {
            // 设置响应头
            setExcelResponseHeaders(response, fileName);
            
            // 创建ExcelWriter
            try (var excelWriter = EasyExcel.write(response.getOutputStream())
                    .excelType(ExcelTypeEnum.XLSX)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .build()) {
                
                // 遍历每个工作表数据进行写入
                for (Map.Entry<String, SheetData<?>> entry : sheetDataMap.entrySet()) {
                    String sheetName = entry.getKey();
                    SheetData<?> sheetData = entry.getValue();
                    
                    // 创建新的工作表
                    try (var writeSheet = EasyExcel.writerSheet(sheetName)
                            .head(sheetData.getDataClass())
                            .build()) {
                        
                        excelWriter.write(sheetData.getData(), writeSheet);
                    }
                }
            }
        } catch (IOException e) {
            log.error("导出多工作表Excel文件失败", e);
            throw new RuntimeException("导出多工作表Excel文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 设置Excel响应头
     */
    private static void setExcelResponseHeaders(HttpServletResponse response, String rawFileName) 
            throws UnsupportedEncodingException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(rawFileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    }

    /**
     * 根据文件名获取Excel类型
     */
    private static ExcelTypeEnum getExcelType(String fileName) {
        if (fileName == null) {
            return ExcelTypeEnum.XLSX;
        }
        if (fileName.toLowerCase().endsWith(".xls")) {
            return ExcelTypeEnum.XLS;
        }
        return ExcelTypeEnum.XLSX;
    }
    
    /**
     * 多工作表数据封装类
     */
    public static class SheetData<T> {
        private final List<T> data;
        private final Class<T> dataClass;
        
        public SheetData(List<T> data, Class<T> dataClass) {
            this.data = data;
            this.dataClass = dataClass;
        }
        
        public List<T> getData() {
            return data;
        }
        
        public Class<T> getDataClass() {
            return dataClass;
        }
    }

    /**
     * Excel数据读取监听器
     */
    private static class ExcelDataListener<T> extends AnalysisEventListener<T> {
        private final List<T> dataBuffer = new ArrayList<>(BATCH_SIZE);
        private final Consumer<List<T>> consumer;
        private int total = 0;
        private static final int BATCH_SIZE = 100;

        public ExcelDataListener(Consumer<List<T>> consumer) {
            this.consumer = consumer;
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            dataBuffer.add(data);
            total++;

            if (dataBuffer.size() >= BATCH_SIZE) {
                consumer.accept(new ArrayList<>(dataBuffer));
                dataBuffer.clear();
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            if (!dataBuffer.isEmpty()) {
                consumer.accept(new ArrayList<>(dataBuffer));
                dataBuffer.clear();
            }
            log.info("Excel数据读取完成，共读取{}条数据", total);
        }

        public int getTotal() {
            return total;
        }
    }
} 